package com.jh.paymentchannel.service;

import cn.jh.common.utils.ResultWrap;
import com.jh.paymentchannel.business.PaymentOrderStatisticBusiness;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.concurrent.Callable;

import static org.springframework.util.StringUtils.hasText;


/**
 * @author Fuchun
 * @since 1.0
 */
@RestController
public class PaymentScheduleService {

    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final PaymentOrderStatisticBusiness paymentOrderStatisticBusiness;

    @Autowired
    public PaymentScheduleService(PaymentOrderStatisticBusiness paymentOrderStatisticBusiness) {
        this.paymentOrderStatisticBusiness = paymentOrderStatisticBusiness;
    }

    /** 每周一上午6点执行统计 */
    @Scheduled(cron = "0 0 6 * * MON", zone = "Asia/Shanghai")
//    @Scheduled(cron = "0 0/30 * * * ?", zone = "Asia/Shanghai")
    public void statsLastWeekOrders() {
        statsLastWeekOrders(LocalDate.now());
    }

    private void statsLastWeekOrders(LocalDate date) {
        wrapExecuteSchedule("statsLastWeekOrders", () -> {
            Pageable pageable = new PageRequest(0, 100, Sort.Direction.ASC, "createTime");
            long total = 0;
            Page<Long> page;
            do {
                page = paymentOrderStatisticBusiness.statsLastWeekOrders(date, pageable);
                if (page.hasContent()) {
                    total += page.getNumberOfElements();
                }
                pageable = page.nextPageable();
            } while (page.hasNext());
            return total;
        });
    }

    /** 每周一上午7点执行统计：推送数据给 user */
    @Scheduled(cron = "0 0 7 * * MON", zone = "Asia/Shanghai")
//    @Scheduled(cron = "0 0/30 * * * ?", zone = "Asia/Shanghai")
    public void syncPosToUser() {
        wrapExecuteSchedule("syncPosToUser", () -> {
            Pageable pageable = new PageRequest(0, 100, Sort.Direction.ASC, "createTime");
            long total = 0;
            long syncCount;
            do {
                syncCount = paymentOrderStatisticBusiness.syncPosToUser(pageable);
                pageable = pageable.next();
                if (syncCount >= 0)
                    total += syncCount;
            } while (syncCount > 0);
            return total;
        });
    }

    /**
     * 手动重新执行统计计算。
     * <pre>{@code
     * POST /v1.0/paymentchannel/stats_orders[?date=yyyy-MM-dd]
     * Response:
     * {
     *     "resp_code": "000000",
     *     "resp_message": "OK"
     * }
     * }</pre>
     *
     * @param dateString 指定的日期 （如果没有此参数，则默认为当天）
     */
    @PostMapping(value = "/v1.0/paymentchannel/stats_orders")
    public Map<String, Object> statsLastWeekOrders(
            @RequestParam(value = "date", required = false) String dateString) {
        LocalDate date = parseDate(dateString, LocalDate.now());
        statsLastWeekOrders(date);
        return ResultWrap.ok();
    }

    /**
     * 手动重新将 PaymentOrderStatistic 数据同步至 user 模块。
     * <pre>{@code
     * POST /v1.0/paymentchannel/send_pos
     * Response:
     * {
     *     "resp_code": "000000",
     *     "resp_message": "OK"
     * }
     * }</pre>
     */
    @PostMapping(value = "/v1.0/paymentchannel/send_pos")
    public Map<String, Object> sendPosDataToUser() {
        syncPosToUser();
        return ResultWrap.ok();
    }

    private LocalDate parseDate(String dateString, @NotNull LocalDate defaultDate) {
        if (!hasText(dateString)) return defaultDate;
        try {
            return LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE);
        } catch (DateTimeException ex) {
            return defaultDate;
        }
    }

    private void wrapExecuteSchedule(String name, Callable<Long> callable) {
        LocalDateTime dateTime = LocalDateTime.now();
        logger.info("Start execute schedule ({}()) at {}", name, dateTime);
        Long records = 0L;
        try {
            records = callable.call();
        } catch (Exception ex) {
            logger.error("Execute `{}()` error {}", name, ex.getMessage());
        }
        logger.info("{} records: {}", name, records);
    }
}
